home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / AIFF_DSP_v15 folder / AIFF_DSP / aiff.c next >
Encoding:
C/C++ Source or Header  |  1994-07-31  |  7.9 KB  |  267 lines  |  [TEXT/MSWD]

  1. /*
  2. FILE:    aiff.c
  3. PROJECT: Ford grant DSP
  4. AUTHOR:  Ben Denckla
  5. COMMENT: generalized code for AIFF-based DSP; contains main()
  6. LINKAGE: interface.c, MacTraps, SANE, ANSI, and a file with
  7.          the "programmer-user variables and functions" listed in aiff.h.
  8. */
  9.  
  10. #include "aiff.h"
  11. #include "interface.h"
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <console.h>
  16. #include <math.h>   // must come before SANE #include to have precedence
  17. #include <SANE.h>
  18.  
  19. #define BUF_SIZ (1L << 14) // buffer size, in frames
  20.  
  21. #define ODDPAD(x) ( (x) + ((x) & 1) ) // rounds up to nearest even #
  22. // needed because if chunk size is odd, there is an uncounted zero pad byte
  23.  
  24. #define FREAD( dat ) fread( &(dat), sizeof (dat), 1, inf )
  25. #define MIN( a, b ) ((a) < (b) ? (a) : (b))
  26.  
  27. #define SAMDAT( action, file ) \
  28.     if (!f##action ( d, ph.framsiz*buflen, 1, file )) \
  29.         err( #action"ing sample data" )
  30.  
  31. basicaiffbeg ba;
  32. privateheader ph;
  33. void *d;            // audio data buffer
  34.  
  35. static FILE *inf, *ouf; // input and output files
  36. static basicaiffbeg init = { 
  37.     {'FORM', 46},  // [frmhd.id], frmhd.si (1,2)
  38.     'AIFF',        // [frm]
  39.     {'COMM', 18},  // [comhd.id], [comhd.si]
  40.     {0},           // com.chan, com.fram, com.wdsi, com.rate
  41.     {'SSND',  8},  // [sndhd.id], sndhd.si (1)
  42.     {0}            // snd.offs, snd.bksi
  43. };
  44. /*
  45. 1. frmhd.si and sndhd.si are initialized to their values assuming a sample
  46.    length of zero bytes.  This way, when the sample length in bytes is
  47.    known, it can just be added to the default values, perhaps adding one
  48.    to frmhd.si (if sndhd.si is odd).
  49. 2. The square brackets around identifiers like "frmhd.id" indicate that
  50.    this field does not change.
  51. */
  52.  
  53. void err ( char *errmsg ) {
  54.     fprintf( stderr, "FATAL ERROR: %s.\n", errmsg);
  55.     exit(0);
  56. }
  57.  
  58. void warn ( char *warnmsg ) {
  59.     fprintf( stderr, "WARNING: %s.\n", warnmsg);
  60. }
  61.  
  62. DEFFUNC( void open_inf( void ) ) {
  63.     char infstr[256];
  64.     Point where = { 64, 64 };
  65.     SFTypeList types = { 'AIFF' };
  66.     
  67.     if ( getfullfstr( where, 1, types, infstr ) )
  68.         err( "getting input file name" );
  69.     if ( !(inf = fopen( infstr, "rb" )) ) err( "opening input file" );
  70. }
  71.  
  72. DEFFUNC( void open_ouf( void ) ) {
  73.     long tmp_ftype;
  74.     Point where = { 64, 64 };
  75.     char oufstr[256] = "Untitled";
  76.  
  77.     if ( putfullfstr( where, "Save AIFF file to:", oufstr ) )
  78.         err( "getting output file name" );
  79.     tmp_ftype = _ftype; // save _ftype
  80.     _ftype = 'AIFF';
  81.     if ( !( ouf = fopen( oufstr, "wb" ) ) ) err( "opening output file" );
  82.     _ftype = tmp_ftype; // restore _ftype
  83. }
  84.  
  85. DEFFUNC( void process_com( ckhd hd ) ) {
  86. // processes information in COMM chunk
  87.     if ( hd.si != init.comhd.si ) warn( "wrong COMM chunk header size" );
  88.     if ( !FREAD( ba.com ) ) err( "reading COMM chunk body" );
  89.     x80tox96( &(ba.com.rate), &(ph.rate) );  // convert float formats
  90.     ph.framsiz = ( (ba.com.wdsi+7) / 8 ) * ba.com.chan;
  91.     printf(
  92.         "\tsampling rate (frames/sec): %f\n"
  93.         "\tword size (bits): %hd\n"
  94.         "\tchannels: %hd\n"
  95.         "\tframe size (bytes): %lu (calculated, not in file)\n"
  96.         "\tsample frames: %lu\n",
  97.         ph.rate, ba.com.wdsi, ba.com.chan, ph.framsiz, ba.com.fram );
  98. }
  99.  
  100. DEFFUNC( void process_snd( ckhd hd ) ) {
  101. // prints information in SSND chunk, seeks past sample data
  102.  
  103.     ba.sndhd.si = hd.si;
  104.     if ( !FREAD( ba.snd ) ) err( "reading SSND chunk body" );
  105.     printf( "\toffset: %lu  block size: %lu\n", ba.snd.offs, ba.snd.bksi );
  106.     if ( ba.snd.bksi )
  107.         puts( "blocksize and offset not supported by this program" );
  108.     ph.samdatpos = ftell( inf );
  109.     if ( fseek( inf, ODDPAD(ba.sndhd.si) - init.sndhd.si , SEEK_CUR) )
  110.         err( "seeking past sample data");
  111. }
  112.  
  113. DEFFUNC( void process_txt( ckhd hd ) ) {
  114. // prints information in a text chunk
  115.     #define TXTBUF_SIZ 80
  116.     char s[TXTBUF_SIZ];
  117.     int txtbufpos, txtbuflen;
  118.     
  119.     hd.si = ODDPAD(hd.si); // OK to include zero pad as part of string in C
  120.     printf( "\ttext: \"" );
  121.     for (txtbufpos = 0; txtbufpos < hd.si; txtbufpos += txtbuflen) {
  122.          txtbuflen = MIN( hd.si - txtbufpos, TXTBUF_SIZ );
  123.         if ( !fread( s, txtbuflen, 1, inf ) ) err( "reading text chunk" );
  124.         printf( "%.*s", txtbuflen, s );
  125.     }
  126.     puts ( "\"" );
  127. }
  128.  
  129. DEFFUNC( void process_ckhd( ckhd hd ) ) {
  130.     char *idstr, i;
  131.     
  132.     printf("\nFound '%.4s' chunk of size %ld\n", &hd.id, hd.si );
  133.     idstr =  (char *) &(hd.id);
  134.     for (i=0; i<4; i++)
  135.         if ( idstr[i] < ' ' || idstr[i] > '~' ) {
  136.             warn( "Forbidden character in ID" );
  137.         }
  138.     if ( idstr[0] == ' ' ) warn( "Forbidden leading space in ID" );
  139. }
  140.  
  141. DEFFUNC( void scan_inf ( void ) ) {
  142. // scans inf, reading in essential header data & finding sample data
  143. // position (does not actually read sample data)
  144.     int comfound = 0, sndfound = 0, fread_res;
  145.     ckhd  hd; long frmck_endpos;
  146.  
  147.     if ( !FREAD( ba.frmhd ) || !FREAD( ba.frm ) )
  148.         err( "reading FORM header or type" );
  149.     process_ckhd( ba.frmhd );
  150.     if ( ba.frmhd.id != init.frmhd.id || ba.frm != init.frm ) {
  151.         warn( "Bad FORM chunk id or type" );
  152.         ba.frmhd.id = init.frmhd.id; ba.frm = init.frm;
  153.     }
  154.     
  155.     frmck_endpos = ba.frmhd.si + sizeof hd;
  156.     while ( ftell( inf ) < frmck_endpos ) {
  157.         if ( !FREAD( hd ) ) err( "reading next chunk header" );
  158.         
  159.         process_ckhd( hd );
  160.  
  161.         switch ( hd.id ) {
  162.             case 'COMM': comfound++; process_com( hd ); break;
  163.             case 'SSND': sndfound++; process_snd( hd ); break;
  164.             case 'NAME':
  165.             case 'AUTH':
  166.             case '(c) ':
  167.             case 'ANNO':             process_txt( hd ); break;
  168.             default: 
  169.                 puts( "\tThis chunk type is not supported by this program" );
  170.                 if ( fseek( inf, ODDPAD(hd.si), SEEK_CUR ) )
  171.                     err( "seeking past unsupported chunk body" );
  172.         }
  173.     }
  174.     puts( "\n" );
  175.     
  176.     
  177.     if ( fgetc( inf ) != EOF ) warn( "File extends beyond FORM chunk" );
  178.     if ( comfound != 1 || sndfound != 1 )
  179.         err( "Not exactly 1 COMM and 1 SSND chunk" );
  180.     if ( ba.com.fram * ph.framsiz != ba.sndhd.si - init.sndhd.si ) {
  181.         warn( "COMM & SSND chunks disagree about sample length" );
  182.         ba.sndhd.si = init.sndhd.si + ba.com.fram * ph.framsiz;
  183.     }
  184.     
  185.     if ( fseek( inf, ph.samdatpos, SEEK_SET ) )
  186.         err( "seeking to beginning of sample data" );
  187.  
  188.     ba.frmhd.si = init.frmhd.si + ODDPAD(ba.com.fram * ph.framsiz);
  189. }
  190.  
  191. DEFFUNC( void write_ouf_hd( void ) ) {
  192.     if ( !fwrite( &ba, sizeof ba, 1, ouf ) ) err( "writing to output file" );
  193. }
  194.  
  195. DEFFUNC( void prog_report( long bufpos ) ) {
  196. // updates (& creates, if necessary) a bar graph of progress
  197.     #define BARMAX  75 // length of progress report bar
  198.     char barlen;
  199.     static char oldbarlen = 0, bar[BARMAX];
  200.  
  201.     if (bufpos == 0) {
  202.         fputs( "Processing...\n", stderr );
  203.         memset( bar, '*', BARMAX );
  204.         fprintf( stderr, "%.*s\n", BARMAX, bar );
  205.     }
  206.     else {
  207.         barlen = (double) (bufpos / ba.com.fram) * BARMAX;
  208.         fprintf( stderr, "%.*s", barlen - oldbarlen, bar );
  209.         oldbarlen = barlen;
  210.         if ( bufpos == ba.com.fram ) fputc( '\n', stderr );
  211.     }
  212.     fflush( stderr );
  213. }
  214.  
  215. DEFFUNC( void pad( FILE *ouf ) ) {
  216.     if ( (ba.com.fram * ph.framsiz) % 2 )
  217.         if ( fputc( 0, ouf ) == EOF ) err( "padding sample data" );
  218. }
  219.  
  220. int main ( int argc, char **argv ) {
  221.     long  bufpos, buflen; // 1,2
  222.  
  223.     ccommand( &argv );
  224.     console_options.title = "\pDenckla Ford Grant AIFF DSP";
  225.     
  226.     ba = init;
  227.  
  228.     if ( take_input ) { open_inf(); scan_inf(); }
  229.  
  230.     init_process();
  231.     
  232.     if ( make_output ) { open_ouf(); write_ouf_hd(); }
  233.     
  234.     if (!(d = malloc( ph.framsiz*BUF_SIZ )))
  235.         err( "allocating memory for sample buffer" );
  236.  
  237.     for (bufpos = 0; bufpos < ba.com.fram; bufpos += buflen) {
  238.         prog_report( bufpos );
  239.          buflen = MIN( ba.com.fram - bufpos, BUF_SIZ ) ;
  240.         if ( take_input )  SAMDAT( read,  inf );
  241.         process_samdat( buflen );
  242.         if ( make_output ) SAMDAT( write, ouf );
  243.     }
  244.  
  245.     prog_report( bufpos );
  246.  
  247.     if ( make_output ) { pad( ouf ); fclose( ouf ); }
  248.     if ( take_input )  fclose( inf );
  249.     free( d );
  250.  
  251.     term_process();
  252.  
  253.     return 0;
  254. }
  255. /*
  256. 1. buffer position: frame # where the next buffer will begin
  257. 2. buffer length: # of meaningful frames in the buffer.
  258.    buflen == BUF_SIZ on all passes except for the last, when buflen ==
  259.    samlen % BUF_SIZ.
  260.    The following is an example for bufpos = 4 and buflen = 2.
  261.    (In reality these quantities will be much larger.)
  262.    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |   (sample frames)
  263.            |-------| | 
  264.             buflen   |
  265.                      bufpos
  266. */
  267.